﻿using System;
using GalaSoft.MvvmLight;
using ErickOrlando.Utilidades.Data;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace ErickOrlando.Utilidades.Mvvm
{
	/// <summary>
	/// Clase base para las clases complejas
	/// </summary>
	public class ComplexClass<T> : INotifyPropertyChanged, IEditableObject, IDisposable, IComplexClass
		where T: new()
	{
		#region Variables
		private string _errorMessage;
		private bool _isCancelEnabled;
		private bool _isDeleteEnabled;
		private bool _isEditEnabled;
		private bool _isEditing;
		private bool _isNew;
		private bool _isNewEnabled;
		private bool _isSaveEnabled;
		private OperationStatus _Status;

		private bool _waitingOperation;

		private T _Entry;

		private T _Selected;
		private ObservableCollection<T> _lista;

		private bool _IsChild;
		#endregion

		#region Constructor
		/// <summary>
		/// Inicia una nueva instancia de ComplexClass
		/// </summary>
		protected internal ComplexClass()
		{
			IsNew = true;
			IsNewEnabled = true;
			DeleteCompletedEvent += OnDeleteCompleted;
			ListCompletedEvent += OnListCompleted;
			SaveCompletedEvent += OnSaveCompleted;
		}
		#endregion

		#region Propiedades

		/// <summary>
		/// Mensaje de Error que de mostrará si no 
		/// se concreta la operación
		/// </summary>
		public string ErrorMessage
		{
			get
			{
				if (Status != null)
					_errorMessage = Status.Message;
				else
					_errorMessage = string.Empty;
				return _errorMessage;
			}
			set
			{
				_errorMessage = value;
				RaisePropertyChanged("ErrorMessage");
			}
		}
		/// <summary>
		/// Estado del Boton Cancelar
		/// </summary>
		public bool IsCancelEnabled
		{
			get { return _isCancelEnabled; }
			set
			{

				_isCancelEnabled = value;
				RaisePropertyChanged("IsCancelEnabled");
			}
		}
		/// <summary>
		/// Estado del Boton Eliminar
		/// </summary>
		public bool IsDeleteEnabled
		{
			get { return _isDeleteEnabled; }
			set
			{

				_isDeleteEnabled = value;
				RaisePropertyChanged("IsDeleteEnabled");
			}
		}
		/// <summary>
		/// Estado del Boton Editar
		/// </summary>
		public bool IsEditEnabled
		{
			get { return _isEditEnabled; }
			set
			{

				_isEditEnabled = value;
				RaisePropertyChanged("IsEditEnabled");
			}
		}
		/// <summary>
		/// Determina si está en el Modo Edición
		/// </summary>
		public bool IsEditing
		{
			get { return _isEditing; }
			set
			{
				_isEditing = value;
				RaisePropertyChanged("IsEditing");
			}
		}
		/// <summary>
		/// Determina si el objeto es nuevo
		/// </summary>
		public bool IsNew
		{
			get { return _isNew; }
			set
			{
				_isNew = value;
				RaisePropertyChanged("IsNew");
			}
		}
		/// <summary>
		/// Estado del Boton Nuevo
		/// </summary>
		public bool IsNewEnabled
		{
			get { return _isNewEnabled; }
			set
			{

				_isNewEnabled = value;
				RaisePropertyChanged("IsNewEnabled");
			}
		}
		/// <summary>
		/// Estado del Boton Guardar
		/// </summary>
		public bool IsSaveEnabled
		{
			get { return _isSaveEnabled; }
			set
			{

				_isSaveEnabled = value;
				RaisePropertyChanged("IsSaveEnabled");
			}
		}
		/// <summary>
		/// Contiene información sobre lo que ocurrió en la BD
		/// </summary>
		public OperationStatus Status
		{
			get { return _Status; }
			set
			{
				_Status = value;
				RaisePropertyChanged("Status");
				RaisePropertyChanged("ErrorMessage");
			}
		}
		/// <summary>
		/// Indicador que muestra un estado de ocupado
		/// cuando se invoca a una operación a la BD
		/// </summary>
		public bool WaitOperation
		{
			get { return _waitingOperation; }
			set
			{

				_waitingOperation = value;
				RaisePropertyChanged("WaitOperation");
			}
		}
		/// <summary>
		/// Objeto que representa la clase Model
		/// </summary>
		protected internal T Entry
		{
			get { return _Entry; }
			set
			{
				_Entry = value;
				BackupEntry = value;
			}
		}

		/// <summary>
		/// Objeto que representa un backup de la clase Model
		/// </summary>
		protected internal T BackupEntry { get; private set; }
		/// <summary>
		/// Representa la Entidad Seleccionada
		/// </summary>
		public T Selected
		{
			get { return _Selected; }
			set
			{
				_Selected = value;
				IsEditEnabled = (value != null);
				IsDeleteEnabled = IsEditEnabled;
				IsNew = !IsEditEnabled;
				RaisePropertyChanged("Selected");
			}
		}

		/// <summary>
		/// Representa la Colección de Elementos para una Clase que lista
		/// </summary>
		public ObservableCollection<T> Lista
		{
			get { return _lista; }
			set
			{
				_lista = value;
				RaisePropertyChanged("Lista");
			}
		}
		/// <summary>
		/// Indica si el objeto es un objeto Hijo
		/// </summary>
		public bool IsChild
		{
			get { return _IsChild; }
			set
			{
				if (_IsChild != value)
				{
					_IsChild = value;
					RaisePropertyChanged("IsChild");
				}
			}
		}

		#endregion

		#region Metodos Virtuales
		/// <summary>
		/// Acción de Cancelar
		/// </summary>
		public virtual void Cancel()
		{
			CancelEdit();
			IsEditing = false;
			Clean();
			EnableStates(false);
		}

		/// <summary>
		/// Acción de Crear un objeto
		/// </summary>
		public virtual void Create()
		{
			IsNew = true;
			IsEditing = true;
			Clean();
			EnableStates(true);
		}
		/// <summary>
		/// Acción de Eliminar un Objeto 
		/// </summary>
		public virtual void Delete()
		{
			if (IsNew)
				throw new InvalidOperationException("No se puede eliminar");
			WaitOperation = true;
		}
		/// <summary>
		/// Acción de Editar un 
		/// </summary>
		public virtual void Edit()
		{
			IsNew = false;
			IsEditing = true;
			BeginEdit();
			EnableStates(true);
		}

		/// <summary>
		/// Accion de Listar un objeto
		/// </summary>
		public virtual void List()
		{
			IsNew = true;
			IsEditing = false;
			Clean();
		}

		/// <summary>
		/// Acción de Guardar un objeto
		/// </summary>
		public virtual void Save()
		{
			IsEditing = false;
			WaitOperation = true;
			if (!IsNew)
				EndEdit();
			EnableStates(false);
			ActualizaHijos();
		}

		/// <summary>
		/// Acción de reinicialización de Valores 
		/// de la propiedad Entry
		/// </summary>
		public virtual void Clean()
		{
			Entry = new T();
			ActualizaStates();
		}

		/// <summary>
		/// Método que se debe implementar
		/// para proporcionar logica de actualizacion de propiedades
		/// </summary>
		public virtual void ActualizaStates()
		{

		}

		/// <summary>
		/// Método que se debe implementar para proporcionar 
		/// logica de actualizacion de objetos hijo
		/// </summary>
		public virtual void ActualizaHijos()
		{

		}
		#endregion

		#region Metodos Privados


		private void EnableStates(bool valor)
		{
			IsNewEnabled = !valor;
			IsEditEnabled = !valor;
			IsSaveEnabled = valor;
			IsCancelEnabled = valor;
			IsDeleteEnabled = !valor;
		}
		#endregion

		#region Eventos

		/// <summary>
		/// Evento que se debe invocar cuando se complete la eliminación
		/// </summary>
		public virtual event Action DeleteCompletedEvent = delegate { };

		/// <summary>
		/// Evento que se debe invocar cuando se complete de cargar la lista
		/// </summary>
		public virtual event Action ListCompletedEvent = delegate { };

		/// <summary>
		/// Evento que se debe invocar cuando se complete la operacion de Guardado
		/// </summary>
		public virtual event Action SaveCompletedEvent = delegate { };


		/// <summary>
		/// Manejador de Eventos para DeleteCompletedEvent
		/// </summary>
		public virtual void OnDeleteCompleted()
		{
			WaitOperation = false;
			Clean();
			if (string.IsNullOrEmpty(ErrorMessage))
				ErrorMessage = "Registro eliminado con éxito!";
		}

		/// <summary>
		/// Manejador de Eventos para ListCompletedEvent
		/// </summary>
		protected virtual void OnListCompleted()
		{
			WaitOperation = false;
		}

		/// <summary>
		/// Manejador de Eventos para SaveCompletedEvent
		/// </summary>
		protected virtual void OnSaveCompleted()
		{
			WaitOperation = false;
			Clean();
			if (string.IsNullOrEmpty(ErrorMessage))
				ErrorMessage = "Registro grabado con éxito!";
			ActualizaStates();
		}

		#endregion

		#region Miembros de INotifyPropertyChanged
		/// <summary>
		/// Evento para manejar los cambios de las propiedades
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged = delegate { };
		/// <summary>
		/// Permite establecer el cambio de un valor en una propiedad
		/// </summary>
		/// <param name="property"></param>
		protected void RaisePropertyChanged(string property)
		{
			PropertyChanged(this, new PropertyChangedEventArgs(property));
		}

		#endregion

		#region Miembros de IEditableObject
		/// <summary>
		/// Inicia la edición del Objeto
		/// </summary>
		public void BeginEdit()
		{
			if (!IsEditing)
			{
				BackupEntry = Entry;
				IsEditing = true;
			}

		}
		/// <summary>
		/// Cancela la edición del Objeto
		/// </summary>
		public void CancelEdit()
		{
			if (IsEditing)
			{
				IsEditing = false;
				Entry = BackupEntry;
			}
		}
		/// <summary>
		/// Termina el proceso de edición
		/// </summary>
		public void EndEdit()
		{
			if (IsEditing)
			{
				IsEditing = false;
				BackupEntry = default(T);
			}
		}

		#endregion

		#region Miembros de IDisposable
		/// <summary>
		/// Destruye la instancia de la clase ComplexClass
		/// </summary>
		public void Dispose()
		{
			GC.Collect();
		}

		#endregion

	}
}